#include "BaseApplication.h"

BaseApplication::BaseApplication() {
    mWindow = NULL;
    mSceneManager = NULL;
    mCamera = NULL;
	mRaySceneQuery = NULL;
	mParticleSystemManager = NULL;
    mViewport = NULL;
    mKeyboard = NULL;
    mMouse = NULL;
    mInputManager = NULL;
    mGui = NULL;
    mRoot = NULL;
    mNetwork = NULL;
	mLuaLink = NULL;
	mSoundManager = NULL;
}

BaseApplication::~BaseApplication() {
    
    cleanup();
    
}

bool BaseApplication::frameStarted(const Ogre::FrameEvent& evt) {
    if (mKeyboard != NULL) {
        mKeyboard->capture();
    }
    if (mMouse != NULL) {
        mMouse->capture();
    }
	if (mGui != NULL && (mLuaLink == NULL || mLuaLink->isGuiActive())) {
        mGui->injectFrameEntered(evt.timeSinceLastFrame);
    }
	if (mLuaLink != NULL) {
		mLuaLink->eachFrame(evt);
	}
	if (mSoundManager != NULL) {
		mSoundManager->update(evt.timeSinceLastFrame);
	}
    return eachFrame(evt);
}

bool BaseApplication::eachFrame(const Ogre::FrameEvent& evt) {
    return running;
}

bool BaseApplication::frameEnded(const Ogre::FrameEvent& evt) {
    return true;
}

bool BaseApplication::mouseMoved(const OIS::MouseEvent& evt) {
    if (mGui != NULL && (mLuaLink == NULL || mLuaLink->isGuiActive())) {
        mGui->injectMouseMove(evt);
    }
	if (mLuaLink != NULL) {
		mLuaLink->mouseMoved(evt);
	}
    return true;
}

bool BaseApplication::mousePressed(const OIS::MouseEvent& evt, OIS::MouseButtonID id) {
    if (mGui != NULL && (mLuaLink == NULL || mLuaLink->isGuiActive())) {
        mGui->injectMousePress(evt, id);
    }
	if (mLuaLink != NULL) {
		mLuaLink->mousePressed(evt, id);
	}
    return true;
}

bool BaseApplication::mouseReleased(const OIS::MouseEvent& evt, OIS::MouseButtonID id) {
    if (mGui != NULL && (mLuaLink == NULL || mLuaLink->isGuiActive())) {
        mGui->injectMouseRelease(evt, id);
    }
	if (mLuaLink != NULL) {
		mLuaLink->mouseReleased(evt, id);
	}
    return true;
}

bool BaseApplication::keyPressed(const OIS::KeyEvent& evt) {
    if (mGui != NULL && (mLuaLink == NULL || mLuaLink->isGuiActive())) {
        mGui->injectKeyPress(evt);
    }
	if (mLuaLink != NULL) {
		mLuaLink->keyPressed(evt);
	}
    
    if (evt.key == OIS::KC_ESCAPE) {
        running = false;
    }
    return true;
}

bool BaseApplication::keyReleased(const OIS::KeyEvent& evt) {
    if (mGui != NULL && (mLuaLink == NULL || mLuaLink->isGuiActive())) {
        mGui->injectKeyRelease(evt);
    }
	if (mLuaLink != NULL) {
		mLuaLink->keyReleased(evt);
	}

    return true;
}

unsigned long BaseApplication::connectionMade(void* client) {
    return 0;
}

unsigned long BaseApplication::connectionFailed(void* client) {
    return 0;
}

unsigned long BaseApplication::dataReceived(void* client, const char* data, unsigned long length) {
    return 0;
}

bool BaseApplication::connectionLost(void* client) {
    return false;
}

void BaseApplication::connectionClosed(void* client) {
    
}

unsigned long BaseApplication::connectionRestored(void* client) {
    return 0;
}

bool BaseApplication::setupRoot() {
    mRoot = new Ogre::Root();
    
    return mRoot != NULL;
}

bool BaseApplication::setupResources() {
    return loadResourceFile("resources.cfg");
}

bool BaseApplication::loadResourceFile(const Ogre::String& fileName) {
    Ogre::String section, type, archive;
    
    Ogre::ConfigFile file;
    file.load(fileName);
    Ogre::ConfigFile::SectionIterator iterator = file.getSectionIterator();
    while (iterator.hasMoreElements()) {
        section = iterator.peekNextKey();
        Ogre::ConfigFile::SettingsMultiMap* settings = iterator.getNext();
        Ogre::ConfigFile::SettingsMultiMap::iterator i;
        for (i = settings->begin(); i != settings->end(); ++i) {
            type = i->first;
            archive = i->second;
            Ogre::ResourceGroupManager::getSingleton().addResourceLocation(
                    archive, type, section);
        }
    }
    
    return true;
}

bool BaseApplication::setupRenderSystem() {
    if (!mRoot->restoreConfig() && !mRoot->showConfigDialog()) {
        return false;
    }
    mRoot->saveConfig();
    return true;
}

bool BaseApplication::setupSoundManager() {
	mSoundManager = OgreOggSound::OgreOggSoundManager::getSingletonPtr();
	mSoundManager->init();
	mSoundManager->setDistanceModel(AL_LINEAR_DISTANCE);

	return true;
}

bool BaseApplication::setupRenderWindow() {
    mWindow = mRoot->initialise(true, "Application");
    return true;
}

bool BaseApplication::setupResourceGroups() {
    Ogre::TextureManager::getSingleton().setDefaultNumMipmaps(5);
    Ogre::ResourceGroupManager::getSingleton().initialiseAllResourceGroups();
    return true;
}

bool BaseApplication::setupScene() {
    mSceneManager = mRoot->createSceneManager(Ogre::ST_EXTERIOR_CLOSE,
            "Default SceneManager");
	
	//mSceneManager->setWorldGeometry("terrain.cfg");

    mCamera = mSceneManager->createCamera("Camera");
    mViewport = mWindow->addViewport(mCamera);
    
    mCamera->setAspectRatio(Ogre::Real(mViewport->getActualWidth()) / 
            Ogre::Real(mViewport->getActualHeight()));
    
    mCamera->setPosition(Ogre::Vector3(0,100,500));
    mCamera->lookAt(Ogre::Vector3(0,0,0));
    mCamera->setNearClipDistance(1);
    mCamera->setFarClipDistance(1000);
	/*
	Ogre::ParticleSystem particulas* = mSceneManager->createParticleSystem("Nimbus", "Examples/GreenyNimbus");
	mSceneManager->getRootSceneNode()->createChildSceneNode()->attachObject(particulas);
*/
    return true;
}

bool BaseApplication::setupSceneTools(){

	mRaySceneQuery = mSceneManager->createRayQuery(Ogre::Ray());
	//mParticleSystemManager = Ogre::ParticleSystemManager::getSingleton();

	return true;
}

bool BaseApplication::setupInputSystem() {
    size_t windowHnd = 0;
    std::ostringstream windowHndStr;
    OIS::ParamList pl;

    mWindow->getCustomAttribute("WINDOW", &windowHnd);
    windowHndStr << windowHnd;
    pl.insert(std::make_pair(std::string("WINDOW"), windowHndStr.str()));
    mInputManager = OIS::InputManager::createInputSystem(pl);
    try {
        mKeyboard = static_cast<OIS::Keyboard*>(
                mInputManager->createInputObject(OIS::OISKeyboard, true));
        mMouse = static_cast<OIS::Mouse*>(
                mInputManager->createInputObject(OIS::OISMouse, true));
        
        mKeyboard->setEventCallback(this);
        mMouse->setEventCallback(this);
    } catch (const OIS::Exception &e) {
		trace("OGRE EXCEPTION ON INPUT SYSTEM:");
		trace(e.eText);
        return false;
    }
    
    return true;
}

bool BaseApplication::setupGui() {
    mGui = new MyGUI::Gui();
    mGui->initialise(mWindow);
    
    return true;
}

bool BaseApplication::setupFrameListener() {
    mRoot->addFrameListener(this);
    return true;
}

bool BaseApplication::setupNetwork(const Ogre::String& host, int port) {
    mNetwork = new NetworkClient(host.c_str(), port, this);
    if (mNetwork->is_running()) {
        return true;
    } else {
        delete mNetwork;
        mNetwork = NULL;
        
        return false;
    }
}

void BaseApplication::startRenderLoop() {
    running = true;
    mRoot->startRendering();
    running = false;
}

void BaseApplication::cleanup() {
    trace("CleaningUp");

	if (mLuaLink != NULL) {
		delete mLuaLink;
		mLuaLink = NULL;
	}

	if (mSoundManager != NULL) {
		delete mSoundManager;
		mSoundManager = NULL;
	}
    
    if (mNetwork != NULL) {
        delete mNetwork;
        mNetwork = NULL;
    }
    
    if (mGui != NULL) {
        mGui->shutdown();
        delete mGui;
        mGui = NULL;
    }
    
    if (mInputManager != NULL) {
        if (mKeyboard != NULL) {
            mInputManager->destroyInputObject(mKeyboard);
        }
        if (mMouse != NULL) {
            mInputManager->destroyInputObject(mMouse);
        }
        OIS::InputManager::destroyInputSystem(mInputManager);
    }

    mKeyboard = NULL;
    mMouse = NULL;
    mInputManager = NULL;
    
    if (mRoot != NULL) {
        mRoot->shutdown();
        
        mRoot = NULL;
    }

	//mSceneManager->destroyQuery(mRaySceneQuery);
	//Me da error al destruir el query... no se porque...
    
    mWindow = NULL;
    mSceneManager = NULL;
    mCamera = NULL;
    mViewport = NULL;
	mRaySceneQuery = NULL;

    trace("Cleanup..OK");
}

bool BaseApplication::createScene() {
    return true;
}

void BaseApplication::startup() {
    if (!setupRoot()) {
        throw Ogre::Exception(52, "Error en la inicializacion!",
                "BaseApplication::setupRoot()");
    }
    trace("SetupRoot..OK");

    if (!setupResources()) {
        throw Ogre::Exception(52, "Error en la inicializacion!",
                "BaseApplication::setupResources()");
    }
    trace("SetupResources..OK");

    if (!setupRenderSystem()) {
        throw Ogre::Exception(52, "Error en la inicializacion!",
                "BaseApplication::setupRenderSystem()");
    }
    trace("SetupRenderSystem..OK");
    
    if (!setupRenderWindow()) {
        throw Ogre::Exception(52, "Error en la inicializacion!",
                "BaseApplication::setupRenderWindow()");
    }
    trace("SetupRenderWindow..OK");
    
    if (!setupResourceGroups()) {
        throw Ogre::Exception(52, "Error en la inicializacion!",
                "BaseApplication::setupResourceGroups()");
    }
    trace("SetupResourceGroups..OK");

    if (!setupScene()) {
        throw Ogre::Exception(52, "Error en la inicializacion!",
                "BaseApplication::setupScene()");
    }
	trace("SetupScene..OK");

	if(!setupSceneTools()){
		throw Ogre::Exception(52, "Error en la inicializacion!",
                "BaseApplication::setupSceneTools()");
	}
    trace("SetupSceneTools..OK");
        
    if (!setupInputSystem()) {
        throw Ogre::Exception(52, "Error en la inicializacion!",
                "BaseApplication::setupInputSystem()");
    }
    trace("SetupInputSystem..OK");

    if (!setupGui()) {
        throw Ogre::Exception(52, "Error en la inicializacion!",
                "BaseApplication::setupGui()");
    }
    trace("SetupGui..OK");

    if (!setupLuaLink()) {
        throw Ogre::Exception(52, "Error en la inicializacion!",
                "BaseApplication::setupLuaLink()");
    }
    trace("SetupLuaLink..OK");

    if (!createScene()) {
        throw Ogre::Exception(52, "Error en la inicializacion!",
                "BaseApplication::createScene()");
    }
    trace("CreateScene..OK");

    if (!setupFrameListener()) {
        throw Ogre::Exception(52, "Error en la inicializacion!",
                "BaseApplication::setupFrameListener()");
    }
    trace("SetupFrameListener..OK");
}

void BaseApplication::setupMouse() {
    const OIS::MouseState &ms = mMouse->getMouseState();
    ms.width = mWindow->getWidth();
    ms.height = mWindow->getHeight();
}

bool BaseApplication::setupLuaLink() {
	mLuaLink = new LuaLink();
	mLuaLink->bind();
	mLuaLink->bind(mSceneManager, mWindow);
	mLuaLink->bind(mGui);
	mLuaLink->bind(mCamera);
	mLuaLink->bind(mRaySceneQuery);

	return true;
}

void BaseApplication::go() {
    startup();
    trace("Startup..OK");
    
    setupMouse();
    
    trace("RenderLoop()");
    startRenderLoop();
    trace("RenderLoop stoped");
}

void BaseApplication::trace(Ogre::String msg) {
#ifdef _DEBUG
	if (mRoot != NULL) {
		Ogre::LogManager::getSingleton().logMessage(msg);
	}
#endif
}
